package com.yupi.yuoj.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.yuoj.common.ErrorCode;
import com.yupi.yuoj.exception.BusinessException;
import com.yupi.yuoj.mapper.ContestRegistrationMapper;
import com.yupi.yuoj.model.entity.Contest;
import com.yupi.yuoj.model.entity.ContestRegistration;
import com.yupi.yuoj.model.entity.ContestSubmit;
import com.yupi.yuoj.model.entity.User;
import com.yupi.yuoj.model.vo.ContestRankVO;
import com.yupi.yuoj.model.vo.UserVO;
import com.yupi.yuoj.service.ContestRegistrationService;
import com.yupi.yuoj.service.ContestService;
import com.yupi.yuoj.service.ContestSubmitService;
import com.yupi.yuoj.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Lazy;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 竞赛注册服务实现
 */
@Service
public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrationMapper, ContestRegistration>
        implements ContestRegistrationService {

    @Resource
    @Lazy
    private ContestService contestService;

    @Resource
    @Lazy
    private ContestSubmitService contestSubmitService;

    @Resource
    private UserService userService;

    @Override
    public int getRegistrationCount(Long contestId) {
        if (contestId == null) {
            return 0;
        }
        QueryWrapper<ContestRegistration> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        return Math.toIntExact(this.count(queryWrapper));
    }

    @Override
    public boolean hasUserRegistered(Long contestId, Long userId) {
        if (contestId == null || userId == null) {
            return false;
        }
        QueryWrapper<ContestRegistration> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("userId", userId);
        return this.count(queryWrapper) > 0;
    }

    @Override
    public ContestRegistration startContest(Long contestId, Long userId) {
        // 1. 验证参数
        if (contestId == null || userId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 2. 检查竞赛是否存在
        Contest contest = contestService.getById(contestId);
        if (contest == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        }

        // 3. 检查竞赛状态
        if (contest.getStatus() != 1) { // 不是进行中状态
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "竞赛尚未开始或已结束");
        }

        // 4. 检查用户是否已注册
        QueryWrapper<ContestRegistration> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("userId", userId);
        ContestRegistration registration = this.getOne(queryWrapper);
        if (registration == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "您尚未注册该竞赛");
        }

        // 5. 检查是否已经开始
        if (registration.getStatus() > 0) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "您已经开始或完成该竞赛");
        }

        // 6. 更新参赛状态为"参与中"
        Date now = new Date();
        registration.setStatus(1); // 参与中
        registration.setStartTime(now);
        this.updateById(registration);

        return registration;
    }

    @Override
    public ContestRegistration finishContest(Long contestId, Long userId) {
        // 1. 验证参数
        if (contestId == null || userId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 2. 检查竞赛是否存在
        Contest contest = contestService.getById(contestId);
        if (contest == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        }

        // 3. 检查用户是否已开始竞赛
        QueryWrapper<ContestRegistration> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("userId", userId);
        ContestRegistration registration = this.getOne(queryWrapper);
        if (registration == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "您尚未注册该竞赛");
        }

        // 4. 检查是否已经开始
        if (registration.getStatus() != 1) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "您尚未开始或已完成该竞赛");
        }

        // 5. 更新完成时间和状态
        Date now = new Date();
        registration.setStatus(2); // 已完成
        registration.setEndTime(now);

        // 6. 更新解题数据
        int solvedCount = contestSubmitService.getUserSolvedCount(contestId, userId);
        int totalPenalty = contestSubmitService.calculateUserTotalPenalty(contestId, userId);

        registration.setTotalScore(solvedCount);
        registration.setTotalPenalty(totalPenalty);

        this.updateById(registration);

        // 7. 更新排名
        updateRankings(contestId);

        return registration;
    }

    @Override
    public List<ContestRankVO> getContestRanking(Long contestId) {
        if (contestId == null) {
            return new ArrayList<>();
        }

        // 1. 获取竞赛
        Contest contest = contestService.getById(contestId);
        if (contest == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        }

        // 2. 检查是否允许查看排名
        if (contest.getVisibleRank() == 0) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "该竞赛不允许查看排名");
        }

        // 3. 获取所有参赛者记录
        QueryWrapper<ContestRegistration> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.orderByAsc("`rank`"); // 使用反引号转义rank关键字
        List<ContestRegistration> registrations = this.list(queryWrapper);

        if (registrations.isEmpty()) {
            return new ArrayList<>();
        }

        // 4. 获取所有用户ID
        List<Long> userIds = registrations.stream()
                .map(ContestRegistration::getUserId)
                .collect(Collectors.toList());

        // 5. 批量获取用户信息
        Map<Long, User> userMap = userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(User::getId, user -> user));

        // 6. 获取每个用户的题目提交情况
        Map<Long, List<ContestSubmit>> userSubmitsMap = new HashMap<>();
        for (Long userId : userIds) {
            List<ContestSubmit> submits = contestSubmitService.getUserContestSubmits(contestId, userId);
            userSubmitsMap.put(userId, submits);
        }

        // 7. 构建排名VO
        List<ContestRankVO> rankVOList = new ArrayList<>();
        for (ContestRegistration registration : registrations) {
            Long userId = registration.getUserId();

            ContestRankVO rankVO = new ContestRankVO();
            rankVO.setUserId(userId);
            rankVO.setRank(registration.getRank());
            rankVO.setScore(registration.getTotalScore());
            rankVO.setTotalPenalty(registration.getTotalPenalty());
            rankVO.setSolvedCount(contestSubmitService.getUserSolvedCount(contestId, userId));
            rankVO.setSubmitCount(userSubmitsMap.getOrDefault(userId, new ArrayList<>()).size());

            // 设置用户信息
            User user = userMap.get(userId);
            if (user != null) {
                UserVO userVO = userService.getUserVO(user);
                rankVO.setUserVO(userVO);
            }

            // 处理每道题的状态
            Map<Long, ContestRankVO.QuestionStatusVO> questionStatusMap = new HashMap<>();
            List<ContestSubmit> userSubmits = userSubmitsMap.getOrDefault(userId, new ArrayList<>());
            
            for (ContestSubmit submit : userSubmits) {
                Long questionId = submit.getQuestionId();
                ContestRankVO.QuestionStatusVO statusVO = questionStatusMap.getOrDefault(questionId, new ContestRankVO.QuestionStatusVO());
                
                statusVO.setQuestionId(questionId);
                statusVO.setAttempts(submit.getAttempts());
                statusVO.setPenalty(submit.getPenalty());
                
                // 如果是第一次AC或者更新AC状态
                if (submit.getIsAccepted() == 1) {
                    statusVO.setSolved(true);
                    statusVO.setAcceptedTime(submit.getSubmitTime());
                } else {
                    statusVO.setSolved(statusVO.getSolved() != null ? statusVO.getSolved() : false);
                }
                
                questionStatusMap.put(questionId, statusVO);
            }
            
            rankVO.setQuestionStatusMap(questionStatusMap);
            rankVOList.add(rankVO);
        }

        return rankVOList;
    }

    @Override
    public boolean updateRankings(Long contestId) {
        if (contestId == null) {
            return false;
        }

        // 1. 获取所有参赛记录
        QueryWrapper<ContestRegistration> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("status", 2); // 只排名已完成的用户
        List<ContestRegistration> registrations = this.list(queryWrapper);

        if (registrations.isEmpty()) {
            return true; // 没有记录，不需要排名
        }

        // 2. 更新每个用户的得分和罚时（确保最新）
        for (ContestRegistration registration : registrations) {
            Long userId = registration.getUserId();
            int solvedCount = contestSubmitService.getUserSolvedCount(contestId, userId);
            int totalPenalty = contestSubmitService.calculateUserTotalPenalty(contestId, userId);

            registration.setTotalScore(solvedCount);
            registration.setTotalPenalty(totalPenalty);
        }

        // 3. 排序：先按解题数量降序，再按罚时升序
        registrations.sort((r1, r2) -> {
            int scoreDiff = r2.getTotalScore() - r1.getTotalScore();
            if (scoreDiff != 0) {
                return scoreDiff;
            }
            return r1.getTotalPenalty() - r2.getTotalPenalty();
        });

        // 4. 设置排名
        for (int i = 0; i < registrations.size(); i++) {
            ContestRegistration registration = registrations.get(i);
            registration.setRank(i + 1);
        }

        // 5. 批量更新
        return this.updateBatchById(registrations);
    }
} 